using System;
using OST.Shared.Interfaces;
using OST.Shared.SharedTypes;

namespace OST.Calc.SunPos
{
    /// <summary>
    /// LowAc-(table based)-Sun Positioning algorithm 
    /// </summary>
    /// <remarks>          
    ///  SunPositionAlgo_LowAc.cpp - Library for calculating the position of the sun
    ///  Created by Gabriel P. Miller, July 22, 2012.
    ///  Attribution-NonCommercial-ShareAlike 3.0 United States (CC BY-NC-SA 3.0) (http://creativecommons.org/licenses/by-nc-sa/3.0/us/)
    ///  
    ///  references2: http://cerebralmeltdown.com/forum/index.php?topic=325.msg1540#msg1540 (accessed 23.02.2014; updated: 13.02.2014)
    /// </remarks>
    public class LowAcSunPosCalculator : ISunPosCalculator
    {

        /// <summary>
        /// HOUR ANGLE TABLE
        /// </summary>
        int[] HourAngleArray = new int[]{
        -601,-721,-840,-957,-1073,-1188,-1301,-1412,-1521,-1629,-1734,-1837,-1937,-2035,-2131,-2224,-2314,-2402,-2486,-2568,-2647,-2723,-2795,-2865,-2931,-2994,-3054,-3110,-3163,-3213,-3259,-3302,-3342,-3378,-3411,-3440,-3466,-3489,-3508,-3524,-3537,-3546,-3552,-3555,-3554,-3551,-3544,-3534,-3522,-3506, 
        -3487,-3466,-3441,-3414,-3384,-3352,-3317,-3279,-3240,-3197,-3153,-3106,-3057,-3006,-2954,-2899,-2842,-2784,-2724,-2662,-2599,-2535,-2469,-2402,-2334,-2264,-2194,-2123,-2051,-1978,-1905,-1831,-1756,-1681,-1606,-1531,-1455,-1380,-1304,-1229,-1154,-1079,-1004,-930,-856,-783,-711,-639,-568,-498, 
        -429,-361,-295,-229,-165,-102,-40,19,78,135,191,244,296,347,395,441,486,528,569,607,643,677,709,739,766,791,814,835,853,868,882,893,901,908,912,913,912,909,903,895,885,873,858,841,822,800,777,751,724,694, 
        663,629,594,557,519,479,437,394,350,304,257,209,160,110,59,7,-44,-97,-151,-205,-259,-313,-368,-422,-477,-531,-585,-638,-691,-744,-795,-846,-896,-946,-994,-1040,-1086,-1131,-1173,-1215,-1255,-1293,-1330,-1365,-1398,-1429,-1458,-1485,-1510,-1533, 
        -1553,-1572,-1588,-1602,-1613,-1622,-1629,-1633,-1635,-1634,-1631,-1625,-1617,-1606,-1592,-1577,-1558,-1537,-1514,-1488,-1459,-1428,-1395,-1359,-1321,-1281,-1238,-1193,-1145,-1096,-1044,-990,-934,-876,-815,-753,-689,-624,-556,-487,-416,-343,-269,-194,-117,-39,40,121,202,285, 
        369,454,539,625,712,800,888,976,1065,1154,1243,1332,1422,1511,1600,1689,1777,1865,1953,2040,2126,2212,2296,2380,2463,2545,2625,2704,2782,2859,2934,3007,3079,3149,3217,3283,3347,3409,3469,3526,3582,3635,3685,3733,3779,3821,3861,3898,3933,3964, 
        3993,4018,4040,4060,4076,4088,4098,4104,4107,4106,4102,4095,4084,4069,4052,4030,4005,3977,3945,3909,3870,3827,3781,3731,3678,3622,3562,3499,3432,3362,3289,3213,3134,3051,2966,2878,2787,2693,2597,2498,2397,2293,2187,2079,1969,1858,1744,1629,1512,1394, 
        1275,1154,1033,910,787,664,540,416,291,167,43,-80,-204,-326,-449
        };

        public SunPosition CalcPos(DateTime dt, double longitude, double latitude)
        {
            var sunPos = new SunPosition() { Timestamp = dt};

            int timezone = 0;

            //unused: out variables
            double h;
            double delta;

            latitude = latitude * Math.PI / 180;

            int[] dayNumArray = new int[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

            int n = dayNumArray[dt.Month - 1] + dt.Day;
            h = (HourAngleArray[n - 1]) / 1000.0;
            h = h + longitude + (timezone * -1 * 15);
            h = ((((dt.Hour + dt.Minute / 60 + dt.Second / 3600) - 12) * 15) + h) * Math.PI / 180;
            double y = (((2 * Math.PI) / 365) * (n - 1));
            delta = (0.006918 - 0.399912 * Math.Cos(y) + 0.070257 * Math.Sin(y) - 0.006758 * Math.Cos(2 * y) + 0.000907 * Math.Sin(2 * y) - 0.002697 * Math.Cos(3 * y) + 0.00148 * Math.Sin(3 * y));

            sunPos.Elevation = (Math.Asin(Math.Sin(latitude) * Math.Sin(delta) + Math.Cos(latitude) * Math.Cos(delta) * Math.Cos(h))) * 180 / Math.PI;
            sunPos.Azimut = (((Math.Atan2((Math.Sin(h)), ((Math.Cos(h) * Math.Sin(latitude)) - Math.Tan(delta) * Math.Cos(latitude))))) * 180 / Math.PI);
            delta = delta * (180 / Math.PI);
            h = h * (180 / Math.PI);

            //norming to NREL
            sunPos.Azimut += 180.0;

            return sunPos;
        }

    }
}
